#include <stdlib.h>
#include <stdio.h>
#include "collaboration.h"
#include "MIDataAccessor.h"
#include "MIUser.h"

MIDataAccessor_impl::MIDataAccessor_impl()
{
	m_reader = new DBAccessor();
	m_reader->create_log();
	m_applications = m_reader->get();
}

MIDataAccessor_impl::~MIDataAccessor_impl()
{
	if (m_reader != NULL)
		delete m_reader;
	
	if (m_applications != NULL)
		delete m_applications;
}

int MIDataAccessor_impl::get_user(collaboration::MIApplication* app, const char* user)
{
	unsigned int i;
	collaboration::MIUser* aux;
	collaboration::ListOfUsers* usrs = &(app->users);
	
	for(i=0; i<usrs->length(); i++)
	{
		aux = &((*usrs)[i]);
		if (strcmp(aux->name, user) == 0)
		{
			return i;
		}
	}

	return -1;
}

int MIDataAccessor_impl::get_role(collaboration::MIApplication* app, const char* role)
{
	unsigned int i;
	collaboration::MIRole* aux;
	collaboration::ListOfRoles* rs = &(app->roles);
	
	for(i=0; i<rs->length(); i++)
	{
		aux = &((*rs)[i]);
		if (strcmp(aux->name, role) == 0)
		{
			return i;
		}
	}

	return -1;
}

int MIDataAccessor_impl::get_application_pos(const char* app)
{	
	unsigned int i;
	collaboration::MIApplication* aux;
	for(i=0; i<m_applications->length(); i++)
	{
		aux = &((*m_applications)[i]);
		if (strcmp(aux->name, app) == 0)
		{
			return i;
		}
	}

	return -1;
}

collaboration::ListOfApplications* MIDataAccessor_impl::applications()
{
	collaboration::ListOfApplications* la = new collaboration::ListOfApplications(*m_applications);
	return la;
}

CORBA::Boolean MIDataAccessor_impl::add_application(const char* app)
{	
	collaboration::MIApplication* apl = get_application(app);
	if (apl != NULL) //solo agregar si no existe ya
	{
		collaboration::MIApplication* aux = new collaboration::MIApplication;
		aux->name = CORBA::string_dup(app);
		
		int len = m_applications->length();
		m_applications->length(len+1);
		(*m_applications)[len] = *aux;
		
		return true;
	}
	
	else return false;
}

CORBA::Boolean MIDataAccessor_impl::remove_application(const char* app)
{
	int pos = get_application_pos(app);
	collaboration::MIApplication* temp;
	if (pos != -1)
	{
		temp = &((*m_applications)[m_applications->length()-1]);
		(*m_applications)[m_applications->length()-1] = (*m_applications)[pos];
		(*m_applications)[pos] = *temp;
		
		m_applications->length(m_applications->length()-1);
		
		return true;
	}
	
	else return false;
}

collaboration::MIConnection* MIDataAccessor_impl::login(const char* app, const char* user, const char* pass)
{
	unsigned int i;
	char* message = NULL;
	bool error = false;
	collaboration::MIUser* usr;
	collaboration::MIApplication* apl;
	collaboration::MIConnection* info_completa = new collaboration::MIConnection;
	
	apl = get_application(app);
	int pos_usr;
	
	if (apl != NULL)
	{
		pos_usr = get_user(apl, user);
		
		if (pos_usr != -1)
		{
			usr = &(apl->users[pos_usr]);
			
			if (strcmp(usr->password, pass) != 0)
			{
				error = true;
				message = CORBA::string_dup("Password incorrecto");
			}
				
			else if (usr->is_online)
			{
				error = true;
				if (message != NULL) CORBA::string_free(message);
				message = CORBA::string_dup("Usuario ya conectado");
			}
		}
		else
		{
			error = true;
			message = CORBA::string_dup("No existe el usuario");
		}
	}
	else
	{
		error = true;
		message = CORBA::string_dup("La aplicacion no existe");
	}

	if (message != NULL)
	{
		info_completa->error_message = CORBA::string_dup(message);
	}
	
	if (!error)
	{
		usr = new collaboration::MIUser;
		MIUser::is_online(usr, true);
		usr->actual_role = CORBA::string_dup(usr->default_role);

		info_completa->valid_login = true;
		info_completa->is_admin = usr->is_admin;
		info_completa->role = CORBA::string_dup(usr->default_role);
		info_completa->allowed_roles = (*allowed_roles(app, user));
		info_completa->connected_users = *(MIApplication::get_connected_users_info(apl));
		
		collaboration::ListOfComponents* componentes = &(apl->components);
		
		int permisoU = -1;
		int permisoR = -1;
		char* componente = NULL;
		for (i = 0; i < componentes->length(); i++)
		{	
			componente = (*componentes)[i].name;
			permisoU = get_user_component_permissions(app, user, componente);
			permisoR = get_role_component_permissions(app, info_completa->role, componente);
					
			if (permisoR == -1)
			{
				if (permisoU == -1) 
					permisoU = apl->default_permissions;
			}
			
			else 
				if (permisoU == -1)
					permisoU = permisoR;
			else 
				if (permisoU < permisoR) 
					permisoU = permisoR;
			
			collaboration::MIComponent* ccc = new collaboration::MIComponent;
			ccc->name = CORBA::string_dup(componente);
			ccc->permissions = permisoU;
			
			collaboration::ListOfComponents* comp = &(info_completa->components);
			int len = comp->length();
			comp->length(len+1);
			(*comp)[len] = *ccc;
			
			info_completa->components = *comp;
		}
		
		if (componente != NULL) CORBA::string_free(componente);
	}

	return info_completa;
}

char* MIDataAccessor_impl::connect_user(const char* app, const char* user)
{	
	collaboration::MIApplication* apl = get_application(app);
	
	if (apl != NULL)
	{
		return MIApplication::set_connected_user(apl, user);
	}
	
	return NULL;
}

void MIDataAccessor_impl::disconnect_user(const char* app, const char* user)
{
	collaboration::MIApplication* apl = get_application(app);
	if (apl != NULL)
	{
		MIApplication::set_disconnected_user(apl, user);
	}
}

void MIDataAccessor_impl::update_user_time(const char* app, const char* user)
{
	collaboration::MIApplication* apl = get_application(app);
	if (apl != NULL)
	{
		MIApplication::update_user_time(apl, user);
	}
}

collaboration::MIConnection* MIDataAccessor_impl::change_connected_user_role(const char* app, const char* user, const char* role)
{
	unsigned int i;
	bool correcto;
	collaboration::MIConnection* info = new collaboration::MIConnection;
	collaboration::MIUser* usr;
	collaboration::MIApplication* apl = get_application(app);
	if (apl != NULL)
	{
		correcto = MIApplication::set_actual_user_role(apl, user, role);

		if (correcto)
		{ // No ha habido error
			int pos = get_user(apl, user);
			usr = &(apl->users[pos]);
			info->valid_login = true;
			info->role = CORBA::string_dup(usr->actual_role);
			info->allowed_roles = (*allowed_roles(app, user));
			
			collaboration::ListOfComponentNames* componentes = application_components(app);
			int permisoU = -1;
			int permisoR = -1;
			char* componente = NULL;
			for (i = 0; i < componentes->length(); i++)
			{
				componente = (*componentes)[i];
				
				permisoU = get_user_component_permissions(app, user, componente);
				permisoR = get_role_component_permissions(app, info->role, componente);
						
				if (permisoR == -1)
				{
					if (permisoU == -1) 
						permisoU = apl->default_permissions;
				}
				else 
					if (permisoU == -1)
						permisoU = permisoR;
				else 
					if (permisoU < permisoR) 
						permisoU = permisoR;
				
				collaboration::MIComponent* ccc = new collaboration::MIComponent;
				ccc->name = CORBA::string_dup(componente);
				ccc->permissions = permisoU;

				collaboration::ListOfComponents* comp = &(info->components);
				int len = comp->length();
				comp->length(len+1);
				(*comp)[len] = *ccc;

				info->components = *comp;
			}
			
			if (componente != NULL) CORBA::string_free(componente);
		}
		
		else
		{
			info->error_message = CORBA::string_dup("Error al cambiar el rol al usuario");
		}
	}
	
	else
	{
		info->error_message = CORBA::string_dup("Aplicacion no encontrada");
	}
	
	return info;
}

collaboration::ListOfUsersAndRoles* MIDataAccessor_impl::connected_users(const char* app)
{
	collaboration::MIApplication* apl = get_application(app);
	if (apl != NULL)
	{
		return MIApplication::get_connected_users(apl);
	}
	else return NULL;
}

collaboration::ListOfUsers* MIDataAccessor_impl::connected_users_info(const char* app)
{
	collaboration::MIApplication* apl = get_application(app);
	if (apl != NULL)
	{
		return MIApplication::get_connected_users_info(apl);
	}
	else return NULL;
}

collaboration::ListOfUsers* MIDataAccessor_impl::users_info(const char* app)
{
	collaboration::MIApplication* apl = get_application(app);
	if (apl != NULL)
	{
		collaboration::ListOfUsers* lu = new collaboration::ListOfUsers(apl->users);
		return lu;
	}
	else return NULL;
}

collaboration::ListOfRoles* MIDataAccessor_impl::roles_info(const char* app)
{
	collaboration::MIApplication* apl = get_application(app);
	if (apl != NULL)
	{
		collaboration::ListOfRoles* lr = new collaboration::ListOfRoles(apl->roles);
		return lr;
	}
	else return NULL;
}

collaboration::MIRole* MIDataAccessor_impl::role_info(const char* app, const char* role_name)
{
	collaboration::MIApplication* apl = get_application(app);

	if (apl != NULL)
	{
		return new collaboration::MIRole(apl->roles[get_role(apl, role_name)]);
	}
	else return NULL;
}

collaboration::MIUser* MIDataAccessor_impl::user_info(const char* app, const char* user_name)
{
	collaboration::MIApplication* apl = get_application(app);

	if (apl != NULL)
	{
		return new collaboration::MIUser(apl->users[get_user(apl, user_name)]);
	}
	else return NULL;	
}

collaboration::ListOfUserNames* MIDataAccessor_impl::role_users(const char* app, const char* role)
{
	collaboration::MIApplication* apl = get_application(app);

	if (apl != NULL)
	{
		return MIApplication::get_role_users(apl, role);
	}
	else return NULL;
}

collaboration::ListOfUserNames* MIDataAccessor_impl::non_updated_users(const char* app)
{
	unsigned int i, len;
	collaboration::ListOfUserNames* usr = new collaboration::ListOfUserNames();
	collaboration::MIApplication* apl = get_application(app);
	collaboration::MIUser* usuario;
	
	if (apl != NULL)
	{
		collaboration::ListOfUsers* v = MIApplication::get_non_updated_users(apl);
		if (v != NULL) 
		{
			for (i = 0; i < v->length(); i++)
			{
				usuario = &((*v)[i]);
				
				len = usr->length();
				usr->length(len+1);
				(*usr)[len] = CORBA::string_dup(usuario->name);
			}
		}
	}
	
	return usr;
}

collaboration::ListOfRoleNames* MIDataAccessor_impl::application_roles(const char* app)
{
	unsigned int i, len;
	collaboration::ListOfRoles* v = NULL;
	collaboration::ListOfRoleNames* rs = new collaboration::ListOfRoleNames();
	
	collaboration::MIApplication* apl = get_application(app);
	collaboration::MIRole* rol;
	if (apl != NULL)
	{
		v = &(apl->roles);
		if (v != NULL)
		{ 
			for (i = 0; i < v->length(); i++)
			{
				rol = &((*v)[i]);
				
				len = rs->length();
				rs->length(len+1);
				(*rs)[len] = CORBA::string_dup(rol->name);
			}
		}
	}
	return rs;
}

collaboration::ListOfUserNames* MIDataAccessor_impl::application_users(const char* app)
{
	unsigned int i, len;
	collaboration::ListOfUsers* v = NULL;
	collaboration::ListOfUserNames* usr = new collaboration::ListOfUserNames();
	
	collaboration::MIApplication* apl = get_application(app);
	collaboration::MIUser* usuario;
	if (apl != NULL)
	{
		v = &(apl->users);
		if (v != NULL)
		{ 
			for (i = 0; i < v->length(); i++)
			{
				usuario = &((*v)[i]);
				
				len = usr->length();
				usr->length(len+1);
				(*usr)[len] = CORBA::string_dup(usuario->name);
			}
		}
	}
	return usr;
}

collaboration::ListOfComponentNames* MIDataAccessor_impl::application_components(const char* app)
{
	unsigned int i, len;
	collaboration::ListOfComponents* v = NULL;
	collaboration::ListOfComponentNames* rs = new collaboration::ListOfComponentNames();
	
	collaboration::MIApplication* apl = get_application(app);
	collaboration::MIComponent* cmp;
	if (apl != NULL)
	{
		v = &(apl->components);
		if (v != NULL)
		{ 
			for (i = 0; i < v->length(); i++)
			{
				cmp = &((*v)[i]);
				
				len = rs->length();
				rs->length(len+1);
				(*rs)[len] = CORBA::string_dup(cmp->name);
			}
		}
	}
	return rs;
}

collaboration::ListOfRoles* MIDataAccessor_impl::allowed_roles(const char* app, const char* user)
{
	int pos;
	unsigned int i, len;
	collaboration::ListOfRoles* todos = roles_info(app);
	collaboration::ListOfRoles* res = new collaboration::ListOfRoles();
	collaboration::MIUser* u_aux;
	collaboration::MIRole* r_aux;
	collaboration::MIApplication* apl = get_application(app);
	
	if ( (todos != NULL) && (apl != NULL) )
	{
		pos = get_user(apl, user);
		if (pos != -1)
		{
			u_aux = &(apl->users[pos]);
			for (i=0; i<todos->length(); i++)
			{
				r_aux = &((*todos)[i]);
			
				if (MIUser::has_allowed_role(u_aux, r_aux->name))
				{
					len = res->length();
					res->length(len+1);
					(*res)[len] = *r_aux; 
				}
			}
			
			return res;
		}
	}
	
	return NULL;
}

collaboration::ListOfRoleNames* MIDataAccessor_impl::allowed_role_names(const char* app, const char* user)
{
	collaboration::MIApplication* apl = get_application(app);
	collaboration::MIUser* usr;
	if (apl != NULL)
	{
		int pos = get_user(apl, user);
		if (pos != -1)
		{ 
			usr = &(apl->users[pos]);
			collaboration::ListOfRoleNames* lrn = new collaboration::ListOfRoleNames(usr->allowed_roles);
			return lrn;
		}
	}
	return NULL;
}

CORBA::Long MIDataAccessor_impl::get_user_component_permissions(const char* app, const char* user, const char* comp)
{
  	collaboration::MIApplication* apl = get_application(app);
	if (apl != NULL)
	{
		return MIApplication::user_component_permissions(apl, user, comp);
	}
	else return -1;
}

CORBA::Boolean MIDataAccessor_impl::set_user_component_permissions(const char* app, const char* user, const char* comp, CORBA::Long level)
{
	collaboration::MIApplication* apl = get_application(app);
	if (apl != NULL)
	{
		return MIApplication::set_user_component_permissions(apl, user, comp, level);
	}
	else return false;
}

CORBA::Long MIDataAccessor_impl::get_role_component_permissions(const char* app, const char* role, const char* comp)
{
	collaboration::MIApplication* apl = get_application(app);
	if (apl != NULL)
	{
		return MIApplication::role_component_permissions(apl, role, comp);
	}
	else return -1;
}

CORBA::Boolean MIDataAccessor_impl::set_role_component_permissions(const char* app, const char* role, const char* comp, CORBA::Long level)
{
	collaboration::MIApplication* apl = get_application(app);
	if (apl != NULL)
	{
		return MIApplication::set_role_component_permissions(apl, role, comp, level);
	}
	else return false;
}

char* MIDataAccessor_impl::user_actual_role(const char* app, const char* user)
{
	collaboration::MIApplication* apl = get_application(app);
	if (apl != NULL)
	{ 
		return MIApplication::get_actual_user_role(apl, user);
	}
	
	return NULL;
}

CORBA::Boolean MIDataAccessor_impl::new_user_allowed_role(const char* app, const char* user, const char* role)
{
	collaboration::MIApplication* apl = get_application(app);
	if (apl != NULL)
		return MIApplication::add_user_allowed_role(apl, user, role);
		
	return false;
}

CORBA::Boolean MIDataAccessor_impl::new_user(const char* app, const char* user, const char* password, const char* default_role, CORBA::Boolean is_admin)
{
	collaboration::MIApplication* apl = get_application(app);

	if (apl != NULL)
	{
		collaboration::MIUser* usr = new collaboration::MIUser;
		usr->name = CORBA::string_dup(user);
		usr->password = CORBA::string_dup(password);
		usr->default_role = CORBA::string_dup(default_role);
		usr->is_admin = is_admin;
		
		return MIApplication::add_user(apl, usr);
	}
	else return false;
}

CORBA::Boolean MIDataAccessor_impl::new_role(const char* app, const char* role)
{
	collaboration::MIApplication* apl = get_application(app);

	if (apl != NULL)
	{
		collaboration::MIRole* rol = new collaboration::MIRole;
		rol->name = CORBA::string_dup(role);
		
		return MIApplication::add_role(apl, rol);
	}
	else return false;
}

CORBA::Boolean MIDataAccessor_impl::remove_allowed_user_role(const char* app, const char* user, const char* role)
{
	collaboration::MIApplication* apl = get_application(app);

	if (apl != NULL)
	{
		return MIApplication::remove_user_allowed_role(apl, user, role);
	}
	
	else return false;
}

CORBA::Boolean MIDataAccessor_impl::remove_user(const char* app, const char* user)
{
	collaboration::MIApplication* apl = get_application(app);

	if (apl != NULL)
	{
		return MIApplication::remove_user(apl, user);
	}
	else return false;
}

CORBA::Boolean MIDataAccessor_impl::remove_role(const char* app, const char* role)
{
	collaboration::MIApplication* apl = get_application(app);

	if (apl != NULL)
	{
		return MIApplication::remove_role(apl, role);
	}
	else return false;
}

collaboration::ListOfApplicationNames* MIDataAccessor_impl::get_applications_names()
{
	unsigned int i, len;
	collaboration::ListOfApplicationNames* res = new collaboration::ListOfApplicationNames();
	
	for (i=0; i<m_applications->length(); i++)
	{
		len = res->length();
		res->length(len+1);
		(*res)[len] = CORBA::string_dup((*m_applications)[i].name);
	}
	
	return res;
}

collaboration::MIApplication* MIDataAccessor_impl::get_application(const char* name)
{
	unsigned int i;
	char* str_aux;
	collaboration::MIApplication* aux;
	
	for (i=0; i<m_applications->length(); i++)
	{
		aux = &((*m_applications)[i]);
		if (aux != NULL)
		{
			str_aux = aux->name;
			if ( (str_aux != NULL) && (strcmp(str_aux, name) == 0) )
			{
				collaboration::MIApplication_var ma = aux;
				return ma._retn();
			}
		}
	}

	return NULL;
}

void MIDataAccessor_impl::commit()
{
	m_reader->set(*m_applications);
}
